package com.jdbc.senior.dao;

import com.jdbc.senior.util.JDBCUtilV2;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

public class BaseDAO {
    public int executeUpdate(String sql, Object... params) throws SQLException {
        Connection connection = JDBCUtilV2.getConnection();

        PreparedStatement preparedStatement = connection.prepareStatement(sql);

        if (params != null && params.length > 0) {
            for (int i = 0; i < params.length; i++) {
                preparedStatement.setObject(i + 1, params[i]);
            }
        }
        int result = preparedStatement.executeUpdate();

        preparedStatement.close();
        JDBCUtilV2.release();

        return result;
    }

    /**
     * 执行 SQL 查询，并将查询结果封装成一个 List<T>，其中 T 是传入的 Class 对象代表的实体类型
     *
     * @param sql SQL 查询语句，可能包含 ? 占位符
     * @param clazz 目标实体的 Class 对象，用于创建实体对象并填充数据
     * @param params SQL 占位符对应的参数值
     * @param <T> 实体类型
     * @return 封装了结果集数据的 List，列表中的每个元素对应一行记录
     * @throws SQLException 数据库操作异常
     * @throws NoSuchMethodException 找不到实体类中的无参构造器时抛出
     * @throws InvocationTargetException 通过构造器调用构造方法时内部方法抛出的异常包装
     * @throws InstantiationException 实例化实体对象时抛出异常
     * @throws IllegalAccessException 当无法访问构造器或字段时抛出异常
     * @throws NoSuchFieldException 找不到实体类中对应字段时抛出异常
     */
    public <T> List<T> executeQuery(String sql, Class<T> clazz, Object... params)
            throws SQLException, NoSuchMethodException, InvocationTargetException,
            InstantiationException, IllegalAccessException, NoSuchFieldException {
        // 1. 获取数据库连接，JDBCUtilV2是自定义的工具类
        Connection connection = JDBCUtilV2.getConnection();

        // 2. 预编译 SQL 语句，创建 PreparedStatement 对象
        PreparedStatement preparedStatement = connection.prepareStatement(sql);

        // 3. 如果 SQL 中有参数，则逐一设置参数值
        if (params != null && params.length > 0) {
            for (int i = 0; i < params.length; i++) {
                // JDBC 参数索引从1开始，这里将每个参数按顺序替换SQL中的?占位符
                preparedStatement.setObject(i + 1, params[i]);
            }
        }

        // 4. 执行查询，返回结果集 ResultSet 对象
        ResultSet resultSet = preparedStatement.executeQuery();

        // 5. 获取结果集的元数据，用于获取列信息（比如列名、列数等）
        ResultSetMetaData metaData = resultSet.getMetaData();
        int columnCount = metaData.getColumnCount();  // 查询结果中列的数量

        // 6. 创建一个 List 来保存查询结果封装后的实体对象
        List<T> list = new ArrayList<>();

        // 7. 遍历结果集中的每一行
        while (resultSet.next()) {
            // 7.1 利用反射调用 clazz 中的无参构造器来创建新实例
            T t = clazz.getConstructor().newInstance();

            // 7.2 遍历当前行中的每一列，依次将列数据设置到实体对象的对应属性中
            for (int i = 0; i < columnCount; i++) {
                // 取出当前列的值，注意 JDBC 的列索引从1开始
                Object object = resultSet.getObject(i + 1);

                // 获取当前列的标签名称，若 SQL 中使用别名，则优先使用别名；否则返回实际的列名
                String columnName = metaData.getColumnLabel(i + 1);

                // 通过字段名（这里假设字段名与列名一致）在目标实体类中查找对应的 Field 对象
                Field declaredField = clazz.getDeclaredField(columnName);
                // 设置为可访问，即使该字段是 private
                declaredField.setAccessible(true);
                // 将当前列的值注入到实例 t 的对应字段中
                declaredField.set(t, object);
            }
            // 将当前实例加入到列表中
            list.add(t);
        }

        // 8. 关闭预编译语句，释放资源
        preparedStatement.close();
        // 释放连接，JDBCUtilV2释放资源的方法（可能关闭连接、返回连接池等）
        JDBCUtilV2.release();

        // 9. 返回封装好的结果集列表
        return list;
    }

    public <T> T executeQueryBean(Class<T> clazz, String sql, Object... params) throws SQLException, NoSuchFieldException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
        List<T> ts = executeQuery(sql, clazz, params);
        if (ts != null && !ts.isEmpty()) {
            return ts.get(0);
        }
        return null;
    }
}
